package com.jacky.bluetooth;

import android.Manifest;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.ScanCallback;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Build;
import android.text.TextUtils;
import android.util.Log;

import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import java.util.List;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;

import static android.bluetooth.BluetoothDevice.TRANSPORT_LE;

/**
 * 使用的是蓝牙的BLE模式， notify
 */
public abstract class BluetoothManager extends BluetoothGattCallback {

    ///蓝牙未连接
    public static final int IDLE = 0;
    ///蓝牙连接中
    public static final int CONNECTING = 1;
    ///蓝牙已连接
    public static final int CONNECTED = 2;
    ///断开蓝牙连接
    public static final int DISCONNECT = 3;
    ///蓝牙连接失败
    public static final int CONNECT_FAIL = 4;
    ///蓝牙连接超时
    public static final int CONNECT_TIMEOUT = 5;
    ///蓝牙已关闭
    public static final int BLUETOOTH_OFF = 6;
    ///蓝牙权限未开启
    public static final int UNAUTHORIZED = 7;
    ///蓝牙已配对
    public static final int PINNED = 8;

    static final int REQUEST_PERSIMISSION = 1112;

    private String BLUE_TOOTH_NAME, charUUid, bluetoothAddress;

    Activity mContet;
    BluetoothGatt mGatt;
    private boolean showLog;

    BluetoothGattCharacteristic MyAttCharacteristic;
    BluetoothAdapter blueadapter;
    private BroadcastReceiver blueReceiver;
    private Timer mTimer;
    private ScanCallback scanCallback;

    private boolean isEnable() {
        if(blueadapter == null) {
            blueadapter = BluetoothAdapter.getDefaultAdapter();
        }
        if(blueadapter == null) {
            print("bluetooth adapter = null");
            return false;
        }
        if(!blueadapter.isEnabled()) {
            print("bluetooth enable...");
            if (!blueadapter.enable()) {
                print("bluetooth enable failure....");
                return false;
            }
        }
        return true;
    }

    public BluetoothManager(Activity context) {
        mContet = context;
    }
    
    public void initParams(String bluetoothName, String characteristicUUid, boolean showLog) {
        if(!bluetoothName.equals(BLUE_TOOTH_NAME)) {
            closeGatt(); //切换连接的蓝牙 则断掉已连接
        }
        BLUE_TOOTH_NAME = bluetoothName;
        charUUid = characteristicUUid;
        this.showLog = showLog;
    }

    public void initParams(String bluetoothName,String bluetoothAddress, String characteristicUUid, boolean showLog) {
        this.bluetoothAddress = bluetoothAddress;
        this.initParams(bluetoothName, characteristicUUid, showLog);
    }

    public int start() {
        if(mGatt != null) {
            BluetoothDevice device = mGatt.getDevice();
            if(BLUE_TOOTH_NAME.equals(device.getName())) {
                if (isConnected(device)) return CONNECTED;
            }
        }

        if(!isEnable()) {
            return BLUETOOTH_OFF;
        }

        Set<BluetoothDevice> devices =  blueadapter.getBondedDevices();
        for(BluetoothDevice device : devices) {
            String name = device.getName();
            String address = device.getAddress();
            print(name + " --- " + address);
            if(BLUE_TOOTH_NAME.equals(name) || (bluetoothAddress != null && bluetoothAddress.equals(address))) {
                startTime(30);
                if(connect(device)) {
                    return CONNECTING;
                }
            }
        }

        if(!blueadapter.isDiscovering()) {
            int f = ContextCompat.checkSelfPermission(mContet, Manifest.permission.ACCESS_FINE_LOCATION);
            if(f == PackageManager.PERMISSION_DENIED) {
                ActivityCompat.requestPermissions(mContet, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_PERSIMISSION);
            } else {
                permissionOK();
            }
        }
        return IDLE;
    }

    /*package*/ void permissionOK() {
        blueReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                String action = intent.getAction();
                if (BluetoothDevice.ACTION_FOUND.equals(action)){
                    BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                    String name = device.getName();
                    String mac = device.getAddress();

                    print(String.format("name : %s, address : %s.", name, mac));
                    if(!TextUtils.isEmpty(name)) {
                        discoverPeripheral(name);

                        if(BLUE_TOOTH_NAME.equals(name)) {
                            if(connect(device)) {
                                cancelDiscovery();
                                connectionStateChangeOnUIThread(CONNECTING);
                            }
                        }
                    }
                }
            }
        };
        IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
        mContet.registerReceiver(blueReceiver, filter);
        blueadapter.startDiscovery();//测试下面代码是否扫描 更快连接上BLE
        startTime(60);
    }

    public boolean connect(BluetoothDevice device) {
        boolean isConnected = mGatt != null && device.equals(mGatt.getDevice()) && isConnected(mGatt.getDevice());
        print("check connect isConnected " + isConnected);
        if(mGatt != null) {
            print("disconnect old gatt.");
            mGatt.disconnect();
            mGatt.close();
        }
        //mGatt.connect()返回true，但实际没有连接效果
//        if(mGatt != null && device.equals(mGatt.getDevice())) {
//            print("gatt connect again..");//上面已经close了 应该无法重新连接
//            return mGatt.connect();
//        } else {
            print("connect gatt ...");
            if(!isEnable()) return false;
            BluetoothDevice remoteDevice = blueadapter.getRemoteDevice(device.getAddress());
            if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                mGatt = remoteDevice.connectGatt(mContet, true, this, TRANSPORT_LE);
            } else {
                mGatt = remoteDevice.connectGatt(mContet, true, this);
            }
            return true;
//        }
    }

    private boolean isConnected(BluetoothDevice device) {
        android.bluetooth.BluetoothManager manager = (android.bluetooth.BluetoothManager)
                mContet.getSystemService(Context.BLUETOOTH_SERVICE);
        int state = manager.getConnectionState(device, BluetoothProfile.GATT);
        return state == BluetoothProfile.STATE_CONNECTED;
    }

    @Override
    public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        print("onCharacteristicRead");
        final  byte[] bytes = characteristic.getValue();
        mContet.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                onValue(bytes);
            }
        });
    }

    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
        print("onCharacteristicChanged");
        final  byte[] bytes = characteristic.getValue();
        mContet.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                onValue(bytes);
            }
        });
    }

    protected abstract void onValue(byte[] bytes);
    protected abstract void connectionStateChange(int newState);
    protected abstract void discoverPeripheral(String bluetoothName);

    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status,final int newState) {
        switch (newState) {
            case BluetoothProfile.STATE_CONNECTED :
                print("bluetooth connect.");
                gatt.discoverServices();
                stopTime();
                cancelDiscovery();
                connectionStateChangeOnUIThread(CONNECTED);
                break;
            case BluetoothProfile.STATE_CONNECTING:
                print("bluetooth connecting.");
                gatt.discoverServices();
                connectionStateChangeOnUIThread(CONNECTING);
                break;
            case BluetoothProfile.STATE_DISCONNECTED :
                print("bluetooth disconnect.");
                stopTime();
                connectionStateChangeOnUIThread(status == 22 ? PINNED : DISCONNECT);
                break;
        }
    }

    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        List<BluetoothGattService> gattServices = gatt.getServices();
        if(gattServices == null) return;
        for(BluetoothGattService service : gattServices) {
//            print(service.getUuid().toString()  + " === "+ service.getType());
            for(BluetoothGattCharacteristic characteristic : service.getCharacteristics()) {
                String id = characteristic.getUuid().toString();
                print("GattCharacteristic UUID : " + id);
                if(charUUid.equals(id)) {
                    MyAttCharacteristic = characteristic;
                    setCharacteristicNotification();
//                    handler.sendEmptyMessage(1);
                    return;
                }
            }
        }
    }

    private void setCharacteristicNotification() {
        boolean isEnableNotification =  mGatt.setCharacteristicNotification(MyAttCharacteristic, true);
        if(isEnableNotification) {
            List<BluetoothGattDescriptor> descriptorList = MyAttCharacteristic.getDescriptors();
            if(descriptorList != null) {
                for(BluetoothGattDescriptor descriptor : descriptorList) {
                    descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                    mGatt.writeDescriptor(descriptor);
                }
            }
        }
    }

    public void stop() {
        cancelDiscovery();
        closeGatt();
        mGatt = null;
        blueadapter = null;
    }

    private void closeGatt() {
        if(mGatt != null) {
            mGatt.disconnect();
            mGatt.close();
        }
    }

    private void startTime(int seconds) {
        print("waiting ble connect...");
        mTimer = new Timer();
        mTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                //定时1分钟， 1分钟时间到则提示超时
                connectionStateChangeOnUIThread(CONNECT_TIMEOUT);
                stopTime();
                closeGatt();
                mGatt = null;
            }
        }, seconds * 1000);
    }

    private void stopTime() {
        if(mTimer != null) mTimer.cancel();
        mTimer = null;
    }

    private void cancelDiscovery() {
        if(blueadapter != null && blueadapter.isDiscovering()) {
            blueadapter.cancelDiscovery();
        }
        if(blueReceiver != null) mContet.unregisterReceiver(blueReceiver);
        blueReceiver = null;

        if(scanCallback != null && blueadapter != null){
            print("blueadapter stopScan");
            blueadapter.getBluetoothLeScanner().stopScan(scanCallback);
        }
        scanCallback = null;
    }

    private void print(String msg) {
        if(showLog)
            Log.d("bluetooth", msg);
    }

    private void connectionStateChangeOnUIThread(final int status) {
        mContet.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                connectionStateChange(status);
            }
        });
    }
}
